package top.yangguangmc.sunshine_anticheat.check.checks.player;

import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockDamageEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.inventory.ItemStack;
import top.yangguangmc.sunshine_anticheat.check.Check;
import top.yangguangmc.sunshine_anticheat.check.CheckCategory;
import top.yangguangmc.sunshine_anticheat.check.CheckSetting;
import top.yangguangmc.sunshine_anticheat.events.ServerTickEvent;
import top.yangguangmc.sunshine_anticheat.utils.Utils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class AutoToolCheck extends Check {
    private final Map<UUID, Long> switchItemTime = new HashMap<>();
    private final Map<UUID, Long> damageBlockTime = new HashMap<>();
    private final Map<UUID, Long> stopDamageBlockTime = new HashMap<>();

    private final Map<UUID, Boolean> isDigging = new HashMap<>();
    private final Map<UUID, Integer> previousToolSlot = new HashMap<>();


    @CheckSetting(configName = "precise-threshold")
    private int threshold = 1;
    @CheckSetting(configName = "min-switch-back-delay")
    private int minBackDelay = 4;

    public AutoToolCheck() {
        super("AutoTool", "auto-tool", CheckCategory.PLAYER);
    }

    @Override
    public void onPacketReceive(PacketReceiveEvent event) {
        if (event.getPacketType() == PacketType.Play.Client.PLAYER_DIGGING) {
            WrapperPlayClientPlayerDigging packet = new WrapperPlayClientPlayerDigging(event);
            if (packet.getAction() == DiggingAction.START_DIGGING) {
                isDigging.put(event.getUser().getUUID(), true);
            } else if (packet.getAction() == DiggingAction.CANCELLED_DIGGING || packet.getAction() == DiggingAction.FINISHED_DIGGING) {
                isDigging.put(event.getUser().getUUID(), false);
                stopDamageBlockTime.put(event.getUser().getUUID(), ServerTickEvent.tickId);
            }
        }
    }

    @EventHandler
    public void onDamageBlock(BlockDamageEvent event) {
        Player player = event.getPlayer();
        if (!validateCheckable(player)) return;
        if (event.getItemInHand() == null || !isRightTool(event.getBlock().getType(), player)) {
            damageBlockTime.put(player.getUniqueId(), ServerTickEvent.tickId);
            switchItemTime.remove(player.getUniqueId());
            return;
        } else damageBlockTime.remove(player.getUniqueId());
//        long time = Math.abs(ServerTickEvent.tickId - switchItemTime.getOrDefault(player.getUniqueId(), 0L));
//        if (time <= thresholdTool && isDigging.getOrDefault(player.getUniqueId(), false)) {
//            failCheck(player, time == 0 ? 2 : 1, "Started damaging a block on the same tick as switching to the best tool. Delta: {} ticks", time);
//            switchItemTime.remove(player.getUniqueId());
//        }
    }

    @EventHandler
    public void onBreakBlock(BlockBreakEvent event) {
        Player player = event.getPlayer();
        if (!validateCheckable(player)) return;
        switchItemTime.remove(player.getUniqueId());
        damageBlockTime.remove(player.getUniqueId());
    }

    @EventHandler
    public void onAttack(EntityDamageByEntityEvent event) {
        if (!validateCheckable(event.getDamager())) return;
        Player player = (Player) event.getDamager();
        damageBlockTime.remove(player.getUniqueId());
        isDigging.put(player.getUniqueId(), false);
    }

    @EventHandler
    public void onSwitchItem(PlayerItemHeldEvent event) {
        Player player = event.getPlayer();
        if (!validateCheckable(player)) return;
        switchItemTime.put(player.getUniqueId(), ServerTickEvent.tickId);
        long time1 = Math.abs(ServerTickEvent.tickId - damageBlockTime.getOrDefault(player.getUniqueId(), 0L));
        if (time1 <= threshold && isDigging.getOrDefault(player.getUniqueId(), false)) {
            failCheck(player, time1 == 0 ? 3 : 1, "Switched to the best tool on the same tick as started to damage a block. Delta: {} ticks", time1);
            damageBlockTime.remove(player.getUniqueId());
            previousToolSlot.put(event.getPlayer().getUniqueId(), event.getPreviousSlot());
        }
        long time2 = Math.abs(ServerTickEvent.tickId - stopDamageBlockTime.getOrDefault(player.getUniqueId(), 0L));
        if (time2 <= minBackDelay && !isDigging.getOrDefault(player.getUniqueId(), false) && event.getNewSlot() == previousToolSlot.getOrDefault(player.getUniqueId(), -1)) {
            failCheck(player, time1 == 0 ? 4 : 2, "Switched back to the previous slot too quickly after stopped damaging a block. Delta: {} ticks", time2);
            stopDamageBlockTime.remove(player.getUniqueId());
        }
    }

    public boolean isPlayerDigging(Player player) {
        return isDigging.getOrDefault(player.getUniqueId(), false);
    }

    private static boolean isRightTool(Material block, Player player) {
        float speed = 0;
        ItemStack best = null;
        ItemStack[] hotbar = Utils.getHotbar(player);
        for (ItemStack item : hotbar) {
            if (item == null) continue;
            float speed1 = getBreakSpeed(block, item);
            if (speed1 > speed) {
                speed = speed1;
                best = item;
            }
        }
        if (best == null) return false;
        return best.equals(player.getItemInHand());
    }

    private static float getBreakSpeed(Material block, ItemStack item) {
        if (item.getType().name().contains("SWORD")) return block == Material.WEB ? 8 : 1;
        else if (item.getType().name().contains("AXE") && !item.getType().name().contains("PICKAXE")) {
            if (block.name().contains("WOOD") || block.name().contains("LOG"))
                return getSpeedOnProperMaterial(item.getType());
        } else if (item.getType().name().contains("PICKAXE")) {
            if (block.name().contains("STONE") || block.name().contains("IRON") || block.name().contains("ANVIL"))
                return getSpeedOnProperMaterial(item.getType());
        } else if (item.getType().name().contains("SHEARS")) {
            if (block.name().contains("WOOL") || block.name().contains("LEAVES")) return 8;
        } else if (item.getType().name().contains("SPADE")) {
            if (block == Material.CLAY || block == Material.GRASS || block == Material.GRAVEL || block == Material.SAND || block == Material.SNOW_BLOCK)
                return 8;
        }
        return 1;
    }

    private static float getSpeedOnProperMaterial(Material toolMaterial) {
        if (toolMaterial.name().contains("WOOD")) return 2;
        else if (toolMaterial.name().contains("STONE")) return 4;
        else if (toolMaterial.name().contains("IRON")) return 6;
        else if (toolMaterial.name().contains("DIAMOND")) return 8;
        else if (toolMaterial.name().contains("NETHERITE")) return 10;
        else if (toolMaterial.name().contains("GOLD")) return 12;
        else return 1;
    }
}
